//
//  ApplicationData.m
//  Asteroids
//
// This is a singleton that is used to keep all of the 'global' application data.
// This singleton will replace the traditional 'C' programming practice of creating
// globnal variables through out the code. In non-object oriented programming languages
// one source file it typically used to declare 'global' application data, and the
// '.h' file would contain the extern statements read by the users of that data.
// Here, we use a class for doing the same thing.
//
//  Created by Charles@Kollar.com on 11/11/10.
//
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

#import "ApplicationData.h"

#define GAME_DATA_FILE_NAME     @"asteroids2.txt"


@implementation ApplicationData

@synthesize rootObj;
@synthesize currentScore;
@synthesize lives;
@synthesize userName;
@synthesize initialAsteroidCount;


//
// These are local methods.. do not export them (list them in the .h file)
//

//
// Methods for reading and writing property lists...
//
// Property lists are written to a file in the space associated with the application.
// The property list (a Dictionary object) can be composed of other NSObjects. The class
// NSPropertyListSerialization will 'serialize' the objects allowing them to be saved
// in the file. So the user doesn't have to 'rebuild' the objects when reading them in,
// as this is done by the NSPropertyListSerialization class.
// We use the property list to save all 'userName' and 'highScore' pairs on this device.
// Other packages (e.g., 'cocos2d Sources'/cocoslive) are available for saving the high
// score to a server. You should note that Apple will require you to warn the user before
// saving any data to an external server. This is typically done through a popup with a
// UIAlertView, a title like "Privacy Warning", and a brief string telling the user that
// what ever their name is it will be posted on the internet for the world to see.

- (NSData *)copyApplicationDataFromFile:(NSString *)fileName { 
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
	
    NSData *myData = [[NSData alloc] initWithContentsOfFile:appFile];
    return myData;
}

- (id)copyApplicationPlistFromFile:(NSString *)fileName {
    NSData *retData;
    NSString *error;
    
	// We can autorelase here because we do not store this in the method...
    retData = [[self copyApplicationDataFromFile: fileName] autorelease];
    if (!retData) {
        NSLog(@"applicationPlistFromFile: Data file not returned, initializing it.");
        return nil;
    }
	
	// this will create objects from the stuff that was stashed on the disk...
    id retPlist = [NSPropertyListSerialization
				propertyListFromData: retData
				// NSPropertyListMutableContainers
				// Causes the returned property list to have mutable containers but immutable leaves.
				// So we need to convert it to a mutableCopy below...
				mutabilityOption: NSPropertyListImmutable // NSPropertyListMutableContainersAndLeaves
				format: NULL // don't save the format
				errorDescription :&error];
	
    // make a mutable copy for us to work with...
    NSLog(@"retPlist  %@ %@", [retPlist class], retPlist);
	
	// Since we may need to change this (user adds a name) we want
	// the data structure to be mutable (changable)...
    NSMutableDictionary *retMPlist = [[NSMutableDictionary alloc] initWithCapacity:[retPlist count]];
	for (id key in retPlist) {
        [retMPlist setValue:[[retPlist valueForKey:key] mutableCopy] forKey:key];
    }

    return retMPlist;
}

- (BOOL)writeApplicationData:(NSData *)data toFile:(NSString *)fileName {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    if (!documentsDirectory) {
        //NSLog(@"Documents directory not found!");
        return NO;
    }
    NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
    return [data writeToFile:appFile atomically:YES];
}

- (BOOL)writeApplicationPlist:(id)plist toFile:(NSString *)fileName {
    NSString *error;
    NSData *pData = [NSPropertyListSerialization
					 dataFromPropertyList: plist
					 format: NSPropertyListBinaryFormat_v1_0 // a binary format available on OS X v10.2 and later
					 errorDescription: &error];
    if (!pData) {
        //NSLog(@"writeApplicatinPlist: %@", error);
        return NO;
    }
    return [self writeApplicationData: pData toFile: fileName];
}

// Write an initial entry to game data file...
-(void) addNewUserEntry:(NSMutableDictionary *)rd name:(NSString*)name score:(int)score {
    NSMutableDictionary *dict = [NSDictionary
                          dictionaryWithObjects:
                          [NSArray arrayWithObjects: [NSNumber numberWithInt:score], [NSNumber numberWithInt:1], nil]
                          forKeys:
                          [NSArray arrayWithObjects:@"HighScore", @"Games", nil]
                          ];
    [rd setObject:dict forKey:name];
}

// because this is a singleton, we do not export the init method.
- (id)init
{ 
	if ((self=[super init])) {
        [self reload];
		[self reset];
		// Variables that are never 'reset'....
		initialAsteroidCount = INITIAL_ASTEROID_COUNT;
	}
	
	return self;
}

// Since this singleton's data lasts for the duration of the application,
// it's dealloc method should never be called. It's included here for completness
- (void) dealloc
{
	[rootObj release];
	[userName release];
	
	[super dealloc];
}

//
// Exported methods...
//

// This is the class method that is used to access the singleton that will hold the
// ApplicationData for the game.
+ (id)shared {
	// There is only one instance ever created of this object. That instance
	// is stored here. The user of this class cannot create an instance of this
	// object (init is not in the .h file). This is a feature and not a bug.
	static ApplicationData *singletonApplicationData = nil;
	
	if (singletonApplicationData == nil)
		singletonApplicationData = [[self alloc] init];
	
    return singletonApplicationData;
}

// This is used by SaveScoreLayer to get a list of users to display in the table that it
// presents the user for selecting a userName for playing under.
- (NSMutableArray *)getLocalUsers {
	NSMutableArray *localUsers = [[NSMutableArray alloc] init];
	for (id key in rootObj) {
		[localUsers addObject: key];
	}
	return localUsers;
}

// Used to add a user to the list of users on the device.
// This is NOT the userName. The userName is the name from the list of names on the device
// that the user has chosen (selected from a table in SaveScoreLayer).
- (void)addUser:(NSString*)name {
	[self addNewUserEntry:rootObj name:name score:0];
	
	// Save user on local machine...
	[self save];
}

// The high score is saved under the username in the 'rootObj' property list
- (int)getHighScore {
	// User must be 'addUser'ed before this will work...
	if (userName == nil) return 0;
	NSDictionary *userObj = [rootObj valueForKey: userName];
	NSNumber *highScore = [userObj valueForKey:@"HighScore"];
	return [highScore intValue];
}

// Sets the high score if applicable (e.g., > current score).
- (void)setHighScore {
	// User must be 'addUser'ed before this will work...
	if (userName == nil) return;
	NSDictionary *userObj = [rootObj valueForKey: userName];
	NSNumber *highScore = [userObj valueForKey:@"HighScore"];
	if (currentScore > [highScore intValue])
		[userObj setValue:[NSNumber numberWithInt:currentScore] forKey:@"HighScore"];
}

// Presumably this is called in the AsteroidLayer class when a missile hits an asteroid
- (void)incrementCurrentScore {
	currentScore += 1;
}

// Presumably this is called when ship hits an asteroid.
- (void)decrementLives {
	lives -= 1;
}

// Used to save the application property list to a file, typically called in the 
// AppDelegate when the applicationWillTerminate.
- (void)save {
	NSLog(@"ApplicationData save: toFile: %@", GAME_DATA_FILE_NAME);
	// Before we quit set the current high score...
	[self setHighScore];
	[self writeApplicationPlist: rootObj toFile: GAME_DATA_FILE_NAME];
}

// Reload the property list information for this game from the file created
// for that purpose.
- (void)reload {
	NSLog(@"ApplicationData reload: fromFile: %@", GAME_DATA_FILE_NAME);
	[rootObj release];
	
	// read in the property list and make the array of users of this device...
	rootObj = [self copyApplicationPlistFromFile: GAME_DATA_FILE_NAME];
	
	// If this is the first time...
	if (rootObj == nil) {
		rootObj = [[NSMutableDictionary dictionaryWithCapacity:1] retain];
	}
}

// Reset the data that get's changed while the game progresses. It's really only
// relevant to split this off of the 'init' method if you are going to restart the
// game for some reason.
- (void)reset {
	currentScore = 0;
	lives = MAX_LIVES;
	userName = nil;
}

@end
